package oop.lab.domain.game;

import java.util.List;
import java.util.stream.Stream;

import oop.lab.domain.board.Board;
import oop.lab.domain.board.Direction;
import oop.lab.domain.board.Location;
import oop.lab.domain.board.Piece;
import oop.lab.domain.board.PieceColor;

public class GomokuGame implements Game {
    private final Board board;
    private boolean finished = false;
    private PieceColor winner = null;
    private PieceColor currentPieceColor;

    public GomokuGame() {
        this.board = new Board(8);
        currentPieceColor = PieceColor.BLACK;
    }

    public String getGameType() {
        return "gomoku";
    }

    public boolean placePiece(Location loc) {
        if (finished) {
            throw new GameException("游戏已经结束");
        }
        if (!board.isValidPlacement(loc)) {
            throw new GameException("无效位置:" + loc.toString());
        }

        board.placePiece(loc, currentPieceColor);
        if (checkWin(loc, currentPieceColor)) {
            finished = true;
            winner = currentPieceColor;
        } else {
            currentPieceColor = currentPieceColor.oppositeColor();
        }
        return true;
    }

    // 计算当前pieces中从起始位置开始指定颜色的连续棋子数量
    private int countHead(PieceColor pieceColor, List<Piece> pieces) {
        int currentCount = 0;
        for (Piece p : pieces) {
            if (p.getColor() == pieceColor) {
                currentCount++;
            } else {
                return currentCount;
            }
        }
        return currentCount;
    }

    // 检查当前位置是否连成五子、即检查当前位置的上+下、左+右、左上+右下、右上+左下是否连成五子
    // 注意：这里使用了函数式编程：Stream API、Lambda表达式
    private boolean checkWin(Location location, PieceColor pieceColor) {
        return Stream.of(Direction.getOppositeDirections())
                .map((Direction[] dirs) -> countHead(
                        pieceColor, board.getLine(location, dirs[0]))
                        + countHead(pieceColor, board.getLine(location, dirs[1])) + 1)
                .anyMatch(c -> (c >= 5));
    }

    public boolean isOver() {
        return finished;
    }

    public PieceColor getWinner() {
        return winner;
    }

    public PieceColor getCurrentPlayer() {
        return currentPieceColor;
    }

    public Board getBoard() {
        return board;
    }
}